#!/usr/bin/env python
# TODO:
# Fix the bug that target directory/photo file name contains white spaces.
"""Program to manipulate photo annotation.

"""
import os
import sys
import commands
import string
import re
import shutil
from datetime import datetime
from datetime import timedelta
from google.python import gflags 

flags = gflags
FLAGS= gflags.FLAGS

flags.DEFINE_string('src_dir', '',
     'source dir for photos')
flags.DEFINE_list('ign_dir', '',
    'the dir we should not annote.')
flags.DEFINE_string('annotation_dir', 'anon_dir', 
    'where to store the annotated photos ')
flags.DEFINE_string('resize_dir', '', 
    'where to store the resized photos')
flags.DEFINE_integer('height', 480,
    'if resize, the new height in pix')
flags.DEFINE_boolean('overwrite', False,
   'if true, overwrite the processed photos.')
flags.DEFINE_boolean('print_year', True,
   'if true, print year, month, day. otherwise month and day.')
flags.DEFINE_integer('time_diff', 0,
    'adjust time difference in hours')
flags.DEFINE_boolean('print_birthday', True,
   'if true, print Claires birthday')

glb_seperate = '_'
CONVERT_BIN = '/usr/bin/convert'
IDENTIFY_BIN = '/usr/bin/identify'

def ResizePhoto(conv_param, dirpath, namelist):
  new_height = conv_param['height'];
  out_sfx = conv_param['out_sfx'];
  dest_dir = conv_param['dest_dir']
  if dirpath.endswith('/'):
    dirpath = dirpath[:-1]

  for name in namelist:
    #print os.path.join(dirpath, name)
    (_, in_sfx) = os.path.splitext(name)
    if not in_sfx in ['.jpg', '.JPG', '.jpeg', '.JPEG']:
      continue

    input_file = os.path.join(dirpath, name)
    print "input file: %s" % (input_file)
    DoResize(input_file, dest_dir, out_sfx, new_height)

def ComputeRealDestDir(src_path, dest_dir_parent):
  """This is to create dest dir for each source dir.
  
  Try to create one level directory tree as the one in the source dir.
  """
  if src_path.endswith('/'):
    # This is necessary because os.path.basename return '' if the
    # dir ends with a slash.
    src_path = src_dir[:-1]
  return os.path.join(dest_dir_parent, os.path.basename(src_path))

def AnotatePhoto(conv_param, dirpath, namelist):
  out_sfx = conv_param['out_sfx'];
  dest_dir_parent = conv_param['dest_dir']
  if dirpath.endswith('/'):
    dirpath = dirpath[:-1]
  dest_dir = ComputeRealDestDir(dirpath, dest_dir_parent)

  for name in namelist:
    (_, in_sfx) = os.path.splitext(name)
    if not in_sfx in ['.jpg', '.JPG', '.jpeg', '.JPEG']:
      continue

    input_file = os.path.join(dirpath, name)
    print_how_old =  FLAGS.print_birthday and not (myflags.ign_dir and dirpath in myflags.ign_dir)
    try:
      DoAnnotation(input_file, dest_dir, out_sfx, print_how_old)
    except ValueError, AttributeError: 
      base_file = os.path.basename(input_file)
      dest_file = os.path.join(dest_dir, base_file)
      if not os.path.exists(dest_dir):
        os.makedirs(dest_dir)
      shutil.copy(input_file, dest_file)
      print "cannot process file %s, copy it to %s" % (input_file,
                                                       dest_file) 
    
def CalcMon(day2, day1):
  delta = (day2 - day1)
  negtive = not delta.days;
  if delta.days < 0:
    (day1, day2) = (day2, day1)
    delta = (day2 -day1);
  days = delta.days;
  y = days / 365
  m = day2.month - day1.month 
  if FLAGS.print_year:
     if m < 0:
       m += 12
  else:
    if m < 0:
       m += (y + 1) * 12
     y = 0
  d = day2.day - day1.day
  if d < 0:
    d += 30
    m -= 1

  if negtive:
    m = 0 - m
  return (y, m, d)
   

def CalcOffset(x,y):
  x = int(x)
  y = int(y)
  if x>y:
    xoffset = int(x*7.8/10)
    yoffset = int(y*9.4/10)
  else:
    xoffset = int(x*6.7/10)
    yoffset= int (y*9.6/10)
  return (xoffset, yoffset)
    
def ShellEscape(filename):
  return filename.replace(" ", r"\ ")
    
def DoAnnotation(imgfile, dest_dir, pfx, print_how_old=True): 
   color = "white";
   myfont = "Candice";
   #add prefix to the output file
   outputfile = ShellEscape(AddPrefix(imgfile, dest_dir, pfx))
   imgfile = ShellEscape(imgfile) 

   if os.path.exists(outputfile) and not myflags.overwrite:
     print "%s exists, ignored" % imgfile
     return
    
   #Get width (x)
   x = commands.getoutput(IDENTIFY_BIN + " -format %w " + imgfile)
   x = int(x)
   #Get height (y)
   y = commands.getoutput(IDENTIFY_BIN + " -format %h " + imgfile)
   y = int(y);
   (xoffset, yoffset) = CalcOffset(x,y)

   #calc font size 
   ptsize = max(x,y) / 48
   
   # Get date from Exif. Try dateTime first, then DateTimeOriginal. Nexus one
   # doesn't have DateTime tag.
   d = commands.getoutput(IDENTIFY_BIN + " -format %[Exif:DateTime] "  + imgfile)
   if not d:
     d = commands.getoutput(" ".join([IDENTIFY_BIN,
                                      r"-format %[Exif:DateTimeOriginal]",
                                      imgfile]))
   d = string.replace(d, '\n','');
   print "Exif: " + d

   #retrieve year, mon, date, h, m, s
   p = re.compile(r'(\d+):(\d+):(\d+)\ (\d+):(\d+):(\d+)')
   m = p.match(d)
   if not m:
     raise ValueError 
   year = m.group(1)
   mon = m.group(2)
   date = m.group(3)
   hour = m.group(4)
   min = m.group(5)
   sec = m.group(6)
   day = datetime(int(year), int(mon), int(date), int(hour), int(min), int(sec))
   taken_day = day + timedelta(hours=myflags.time_diff)
   
   if int(taken_day.strftime("%H")) < 12 :
     ampm = "am"
   else:
     ampm = "pm"
   ampm += '  '

   note = day.strftime("%Y-%m-%d %H:%M")+ampm
   
   #calc how old is Claire
   claire_birthday = datetime(2007, 8, 8)
   if print_how_old:
     (byear, bmon, bday) = CalcMon(taken_day, claire_birthday)
     howold = ''
     if byear > 0:
       howold += str(byear) + 'y '
     howold += str(bmon)+'m '
     if bday > 0:
       howold += str(bday)+'d  \n'
   else:
     howold =''

   note += '\n' + howold 

   exec_str = ' '.join([CONVERT_BIN,
                        imgfile, 
                        "-font", myfont, 
                        "-fill", color, 
                        '-pointsize', str(ptsize),
                        '-annotate +%s+%s' % (xoffset, yoffset),
                        '\'%s\'' % str(note), 
                        outputfile]) 
   print exec_str
   commands.getoutput(exec_str)
   print "anon: %s ===> %s" % (imgfile, outputfile)
   print '*'*60


def DoResize(input_file, dest_dir, out_sfx, height):
   outputfile = AddPrefix(input_file, dest_dir, out_sfx) 

   exec_list = [CONVERT_BIN, "-resize ", 'x'+str(height), input_file, outputfile];
   exec_str = ' '.join(exec_list);
   print exec_str

   commands.getoutput(exec_str)
   print "resize: %s ==> %s" % (input_file, outputfile)
   print '-'*60


def AddPrefix(input_file, dest_dir, pfx):
   (dirname, filename) = os.path.split(input_file)
   if not os.path.exists(dest_dir):
      os.makedirs(dest_dir)
   return os.path.join(dest_dir, pfx + glb_seperate+ filename)


class Flags:
  def __init__(self):
    self.anon_dir = 'anon_dir'
    self.resize_dir = 'resize_dir'
    self.height = 800
    self.ign_dir = []
    self.resize_dir = ''
    self.time_diff = 0 
    self.overwrite = True 

myflags=Flags()

def main(argv):
  argv = FLAGS(argv)           # handles help as well

  if (not os.path.exists(CONVERT_BIN) or
      not os.path.exists(IDENTIFY_BIN)):
    print "No imagemagick found. Please install it."
    sys.exit(1)

  if not FLAGS.src_dir:
    print "Specify the source dir please"
    sys.exit(1)

  myflags.anon_dir = FLAGS.annotation_dir
  myflags.resize_dir = FLAGS.resize_dir
  myflags.height = FLAGS.height
  myflags.ign_dir = FLAGS.ign_dir
  myflags.time_dir = FLAGS.time_diff
  myflags.overwrite = FLAGS.overwrite

  convp1 = {'out_sfx': 'anon',
            'dest_dir': myflags.anon_dir}; 
  convp2 = {'height': myflags.height, 
            'out_sfx': 'resize',
            'dest_dir': myflags.resize_dir}; 

  os.path.walk(FLAGS.src_dir, AnotatePhoto, convp1)
  if FLAGS.resize_dir:
    os.path.walk(convp1['dest_dir'], ResizePhoto, convp2)

 
if __name__ == '__main__':
  main(sys.argv)
